ICTSC2018 本戦 問題解説: 先輩、ファイルが転送できないです!

問題文

あなたの後輩が「FTPが動かない」と相談してきました。後輩の頼みを断れないあなたはこの問題を見てみることになりました。
どうやら家の外から認証は出来そうなのですが、ディレクトリ一覧を見ることが出来なさそうです。

手元PCから192.168.7.1で接続し、ディレクトリ一覧を取得できるようにしてください。

情報

server

routerがport forwardingを行っているため、routerのIPアドレスにSSHをすればserverに接続することが可能です。

IPアドレス: 192.168.7.1
ユーザー: admin
パスワード: cf1e8c14e54505f6

router

もしログインするのであれば、serverを踏み台にしてアクセスしてください。

IPアドレス: 192.168.7.254
ユーザー: admin
パスワード: f3395cd54cf857dd

トポロジー図

禁止行為

  • 以下の設定を変更してはいけない。
connect_from_port_20=YES
  • 以下のルールを削除・無効化してはいけない。
iptables -A OUTPUT -p tcp --sport 20 -j REJECT

解説

問題文に「家の外から認証は出来そうなのですが、」とあるように、家の外から接続することが出来ないようです。家の外で接続する場合、途中でアドレス変換(NAT)されたり、routerに入力されるパケット(inboundのパケット)を制限されている場合があります。そのため、ACTIVEモードよりもPASVモードで接続する方が接続しやすいでしょう。

まず、192.168.7.1にFTPで接続してみます。

$ ftp 192.168.7.1
Connected to 192.168.7.1.
220 (vsFTPd 3.0.3)
Name (192.168.7.1:root): admin
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pass
Passive mode on.
ftp> ls
227 Entering Passive Mode (192,168,7,129,169,115).
ftp: connect: Connection timed out
ftp>

すると、227 Entering Passive Modeというレスポンスが表示された後タイムアウトになります。このレスポンスの後方にある6つの数字は、PASVモードで接続する際に使用するIPアドレスとポート番号を表しています。RFC959を見てみると次のように説明されています。

DATA PORT (PORT)

The argument is a HOST-PORT specification for the data port
to be used in data connection. There are defaults for both
the user and server data ports, and under normal
circumstances this command and its reply are not needed. If
this command is used, the argument is the concatenation of a
32-bit internet host address and a 16-bit TCP port address.
This address information is broken into 8-bit fields and the
value of each field is transmitted as a decimal number (in
character string representation). The fields are separated
by commas. A port command would be:

PORT h1,h2,h3,h4,p1,p2

where h1 is the high order 8 bits of the internet host
address.

つまり、(192,168,7,129,169,115)という6つ組の数字は、「PASV接続する時は、192.168.7.129:43379に接続してください」という意味になっています。

ですが、192.168.7.129というのは、NAT内でのIPアドレスなので、NATの外から直接接続することはできません。本来であれば、192.168.7.1と表示されてほしいはず。192.168.7.1にアクセスした後に192.168.7.129にパケットが転送されてくれないと困るため、routerに対してrouterが着信した通信を転送する設定(Port forwarding)を入れる必要もありそうです。また、問題文からserverにもiptablesの設定が入っていそうなので、その設定も追記をする必要があるかもしれません。

まず始めに、serverの/etc/vsftpd.confに以下の設定をします。詳しい設定の意味は、こちらを確認すると良いです。ざっくり説明すると「vsftpdはスタンドアロンで動作し、IPv4からの接続を受け付ける。また、PASV接続の際には192.168.7.1の60001~60025番ポートを使用する」という意味になります。この設定を入れたあとは、vsftpdを再起動してください(sudo systemctl restart vsftpd)。

- LISTEN=NO
+ LISTEN=YES
- LISTEN_IPv6=YES
+ LISTEN_IPv6=NO
...
+ pasv_address=192.168.7.1
+ pasv_min_port=60001
+ pasv_max_port=60025

この次に、serverのiptablesの設定を確認してみましょう。

$ sudo iptables -L
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT tcp -- anywhere anywhere tcp dpt:ftp
ACCEPT tcp -- anywhere anywhere tcp dpt:ssh
ACCEPT tcp -- anywhere anywhere tcp dpt:domain
ACCEPT udp -- anywhere anywhere udp dpt:domain
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination
REJECT tcp -- anywhere anywhere tcp spt:ftp-data /* DON'T REMOVE IT. */ reject-with icmp-port-unreachable

今回、60001番から60025番ポートをFTP用に使おうとしているので、それを許可する設定を入れる必要があります。

$ sudo iptables -A INPUT -p tcp --dport 60001:60025 -j ACCEPT

最後にrouterのPort forwardingの設定を確認してみましょう。

$ sudo iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DNAT tcp -- anywhere router tcp dpt:ftp to:192.168.7.129
DNAT tcp -- anywhere router tcp dpt:ssh to:192.168.7.129

Chain INPUT (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 192.168.7.128/25 anywhere

先程と同様に、60001番から60025番ポートを転送するような設定を入れる必要があります。

$ sudo iptables -t nat -A PREROUTING --dst 192.168.7.1 -p tcp --dport 60001:60025 -j DNAT --to 192.168.7.129

ここまで設定を入れると、次のようにPASVモードで接続できていることが分かります。

ftp 192.168.7.1
Connected to 192.168.7.1.
220 (vsFTPd 3.0.3)
Name (192.168.7.1:root): admin
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pass
Passive mode on.
ftp> ls
227 Entering Passive Mode (192,168,7,1,234,102).
150 Here comes the directory listing.
226 Directory send OK.
ftp>

想定解法

  • serverの/etc/vsftpd.confに以下の設定を入れた。
- LISTEN=NO
+ LISTEN=YES
- LISTEN_IPv6=YES
+ LISTEN_IPv6=NO
...
+ pasv_address=192.168.7.1
+ pasv_min_port=60001
+ pasv_max_port=60025
  • serverのiptablesに以下の設定を入れた。
sudo iptables -A INPUT -p tcp --dport 60001:60025 -j ACCEPT
  • routerのiptablesに以下の設定を入れた。
sudo iptables -t nat -A PREROUTING --dst 192.168.7.1 -p tcp --dport 60001:60025 -j DNAT --to 192.168.7.129

講評

解答率はだいたい30%くらいでもうちょっと解いてくれる人多いかなと期待してました。作問者自身も中学生とかの頃はよく使っていましたが、最近はsftpで済ませてしまうことがほとんどなので、最近触れる人は意外とFTPを使ったことがないのかもしれないと考えています。

この問題は想定解法以外にもいくつかの解法があります。想定解法ではPASVモードで接続するようにしているのですが、ACTIVEモードで接続するのもこの問題のゴールである「手元PCから192.168.7.1で接続し、ディレクトリ一覧を取得できるようにしてください。」を満たしているため正解としています。中には、ip_conntrack_ftpip_nat_ftpを用いた解法もありましたが、基本的に使っても接続できるようにはならないはずです。想定解法以外の解法についてはいつかどこかで説明できたらなと思っています。